home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / printing / hairlines / hairlines.p < prev    next >
Encoding:
Text File  |  2000-06-23  |  18.5 KB  |  666 lines

  1. {
  2.     File:        HairLines.p
  3.  
  4.     Contains:    HairLines demonstrates the proper way to draw hairlines on both
  5.                 PostScript and QuickDraw printers.
  6.  
  7.     Written by: Dave Hersey    
  8.  
  9.     Copyright:    Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 7/26/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.                 6/16/91, DMH            - Revised comments throughout source code.  No actual code changes.
  23.                 
  24.                 5/9/91    DMH                - Revised comments about SetLineWidth.  The minimum value is
  25.                                             1/128, not 1/28.
  26.                                          - Fixed the drawing routine so that it doesn't box rectangles that
  27.                                             have collapsed on themselves.  There were logic bugs.
  28.                                             
  29.                 5/6/91    DMH                - Added missing DisposHandle call for widHdl in DrawStuff.
  30.                                         - Fixed scaling problems so that all methods scale alike.
  31.                                         - Added 'vers' and 'SIZE' resources.
  32.                                         - Adjusted SetLineWidth scale values so that true hairlines are created.
  33.     
  34.                 
  35.                 
  36. }
  37.  
  38. PROGRAM HairLines;
  39.  
  40. USES
  41.     Memory, QuickDraw, Traps, Printing, Packages, PrintComments, Fonts, TextUtils, Devices;
  42.  
  43. CONST
  44.  
  45.     {The following constants are used to identify menus and their items. The menu IDs
  46.      have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  47.  
  48.     rMenuBar    = 128;                    {menubar}
  49.  
  50.     mApple        = 128;                    {Apple menu}
  51.     iAbout        = 1;
  52.  
  53.     mFile        = 129;                    {File menu}
  54.     iPrint        = 1;
  55.     iQuit        = 3;
  56.  
  57.     mEdit        = 130;                    {Edit menu}
  58.  
  59.     mSettings    = 131;                    {Settings menu}
  60.     iuseSetLine    = 1;
  61.     iusePrGen    = 2;
  62.     iuseScale    = 3;
  63.  
  64.  
  65. VAR
  66.  
  67.     gQuitting    : Boolean;                {"Are we all done?" flag}
  68.  
  69.  
  70. {*------ DrawStuff -----------------------------------------------------------------*}
  71.  
  72. {**
  73.  **      DrawStuff draws the objects.  If doSetLineWidth is true, we make SetLineWidth
  74.  **        PicComment calls.  The doPrGeneral flag is just passed in for the text output.
  75.  **        prRsl is the resolution of the printer's GrafPort and is used to determine
  76.  **        the amount to scale everything.
  77.  **}
  78.  
  79. {$S Main}
  80.  PROCEDURE DrawStuff(theWorld : Rect; theGPort : GrafPtr; doPrGeneral, doSetLineWidth, doScale: Boolean; prRsl : Integer);
  81.  
  82.  VAR
  83.     oldPort        : GrafPtr;
  84.     Width        : Widhdl;
  85.     aRect        : Rect;
  86.     count        : Integer;
  87.     scaleFactor    : Comp;
  88.     fNum        : Integer;
  89.     numStr        : Str255;
  90.     hInc, vInc    : Integer;
  91.     temp        : Integer;
  92.  
  93.  BEGIN
  94.  
  95. {Get the current port and save it.  Set the port to our drawing GrafPort and clip
  96.  it to the world's bounds.  Finally, make sure we're using normal pen settings.}
  97.  
  98.     GetPort(oldPort);
  99.     SetPort(theGPort);
  100.     ClipRect(theWorld);
  101.     PenNormal;
  102.  
  103.  
  104. {If we're supposed to be using the SetLineWidth PicComment, then use it.  The
  105.  pen size is already set to (1, 1) by the PenNormal call above.  When this prints
  106.  it will be treated as a 4 pixel pen size.  By passing a pen size scalar with the
  107.  SetLineWidth PicComment, we can make this smaller.  The smallest scalar that 
  108.  SetLineWidth recognizes is 1/128th.  Anything smaller may cause problems.  In
  109.  practice, values from 1/26 to 1/128 yield the same results.  The SetLineWidth
  110.  PicComment is currently only supported by Postscript printer drivers, and is
  111.  explained in Tech Note #91.}
  112.  
  113.     Width := widhdl(NewHandle(sizeof(widpt)));
  114.  
  115.     Width^^.h := 28;                {denominator.  1/128th is max. value of scalar,
  116.                                       but 1/28 yields the same results on a standard
  117.                                      LaserWriter.}
  118.  
  119.     Width^^.v := Round(prRsl/72);    {numerator.  If we're not at 72 dpi, we need
  120.                                      to adjust the penSize scalar for the printer's
  121.                                      coordinate system.}        
  122.  
  123.     IF (doSetLineWidth) THEN PicComment(SetLineWidth, 4, Handle(width));
  124.  
  125.  
  126. {We need to make sure that our graphics are scaled correctly for the printer's
  127.  resolution.  They were created at 72 dpi, (the standard), and need to be scaled
  128.  based on the printer port's resolution.  The port defaults to 72 dpi,
  129.  but we may have used PrGeneral to increase this resolution.  If we passed
  130.  a FALSE doScale value to this routine, no scaling will be done.  This shows
  131.  the effect of using PrGeneral to set the printer to a higher resolution
  132.  without scaling.  Normally, you wouldn't want to do this.  It's just here for
  133.  demonstration purposes.}
  134.  
  135.     IF doScale THEN
  136.         scaleFactor := prRsl div 72
  137.     ELSE
  138.         scaleFactor := 1;
  139.  
  140.  
  141. {Set aRect to the area we'll draw in.  In this case, it's the entire page.
  142.  Next, move the pen to the bottom of the page and set the Times font.
  143.  Notice that we need to scale the font size just as we would any other
  144.  graphic when using a non-72 dpi printer port.  If we don't, things will
  145.  be scaled wrong.}
  146.  
  147.     aRect := theWorld;
  148.  
  149.     MoveTo(aRect.left +Round(5 * scaleFactor), aRect.bottom -Round(10 * scaleFactor));
  150.     GetFNum('Times', fnum);
  151.     TextFont(fNum);
  152.     TextSize(Round(12 * scaleFactor));
  153.  
  154.  
  155. {Draw a string that explains the graphic being printed.  If non-hairlined
  156.  objects are being printed, we just say that.  Otherwise, we give other
  157.  vitals such as what methods were used and the resolution the test pattern
  158.  was printed at.}
  159.  
  160.     IF (NOT doPrGeneral AND NOT doSetLineWidth) THEN
  161.         DrawString('An example of non-hairlined objects.')
  162.     ELSE
  163.     BEGIN
  164.     
  165.         DrawString('An example of hairlined objects printed at ');
  166.  
  167.         NumToString(prRsl, numStr);
  168.         DrawString(numStr);
  169.         DrawString(' dpi ');
  170.     
  171.         IF (NOT doSetLineWidth) THEN DrawString('w/o ');
  172.             
  173.         DrawString('using the SetLineWidth PicComment, ');
  174.         
  175.         IF (NOT doPrGeneral) THEN DrawString('w/o ');
  176.     
  177.         DrawString('using PrGeneral.');
  178.  
  179.     END;
  180.  
  181.  
  182. {Draw a string saying we didn't scale if we didn't and we should have.
  183.  This way, the user knows why the page looks funny.  We scale our font
  184.  size here, no matter what doScale says.  Otherwise they won't be able
  185.  to read the message.  Also bop the top of the drawing area down a bit
  186.  so we won't draw over our message.}
  187.  
  188.     IF (NOT doScale AND doPrGeneral) THEN
  189.     BEGIN
  190.  
  191.         MoveTo(5 * Round(prRsl/72), 20 * Round(prRsl/72));
  192.         TextSize(12 * Round(prRsl/72));
  193.         DrawString('Graphics not appropriately scaled for PrGeneral.');
  194.         aRect.top := aRect.top + 30 * Round(prRsl/72);
  195.  
  196.     END;
  197.  
  198.  
  199. {Now we actually draw the objects.  This is just a bunch of lines, rectangles
  200.  and ovals that make a quasi-neat pattern.  We move the bottom of the drawable
  201.  up a tad so that we don't draw over our text.  hInc and vInc are the increments
  202.  to move in after drawing each set of objects.}
  203.  
  204.     aRect.bottom := aRect.bottom -Round(30 * scaleFactor);
  205.  
  206.     hInc := 10;
  207.     vInc := 15;
  208.  
  209.     FOR count := 1 TO 35 DO
  210.     BEGIN
  211.  
  212. {The check below tells us if the left and right of aRect have crossed for
  213.  the first time.  If so, we change our increments to expand the graphics
  214.  back outward.}
  215.  
  216.         IF (aRect.left > aRect.right) OR (aRect.top > aRect.bottom) THEN
  217.         BEGIN
  218.             
  219.             IF (aRect.top > aRect.bottom) THEN
  220.             BEGIN
  221.  
  222.                 temp := aRect.top;
  223.                 aRect.top := aRect.bottom;
  224.                 aRect.bottom := temp;
  225.                 hInc := 5;
  226.                 vInc := -9;
  227.  
  228.             END;
  229.  
  230.             IF (aRect.left > aRect.right) THEN
  231.             BEGIN
  232.  
  233.                 temp := aRect.left;
  234.                 aRect.left := aRect.right;
  235.                 aRect.right := temp;
  236.                 hInc := -5;
  237.                 vInc := 9;
  238.  
  239.             END;
  240.         END;
  241.  
  242.  
  243. {Draw the objects.  When done, dispose of our width handle and restore the old
  244.  graphics port.}
  245.  
  246.         InsetRect(aRect, Round(scaleFactor * hInc), Round(scaleFactor * vInc));
  247.  
  248.  
  249. {Draw a rectangle and an oval, but only do this if our rectangle hasn't been
  250.  inset so much that its sides have collapsed.  Otherwise, what QuickDraw and
  251.  a particular driver do will be unpredictable.}
  252.  
  253.         IF (hInc > 0) AND (vInc > 0) THEN
  254.         BEGIN
  255.  
  256.             FrameRect(aRect);
  257.             FrameOval(aRect);
  258.  
  259.         END;
  260.  
  261.         MoveTo(aRect.left +1, aRect.top +1);
  262.         LineTo(aRect.right -1, aRect.bottom -1);
  263.         MoveTo(aRect.left +1, aRect.bottom -1);
  264.         LineTo(aRect.right -1, aRect.top +1);
  265.  
  266.     END;
  267.  
  268.     IF (Width <> nil) THEN DisposeHandle(Handle(Width));
  269.     SetPort(oldPort);
  270.  
  271.  END;    {**  DrawStuff  **}
  272.  
  273.  
  274. {*------ GetBestRsl -----------------------------------------------------------------*}
  275.  
  276. {**
  277.  **      GetBestRsl determines the best "square" resolution supported by the printer.
  278.  **        For example, 300 dpi horizontal by 300 dpi vertical.  It isn't necessary to
  279.  **        use square resolutions, but it generally proves easier.  We use PrGeneral and
  280.  **        the getRslDataOp opCode to get a list of the supported resolutions for our
  281.  **        printer.  Then we just go through the rgRslRec and find the maximum square
  282.  **        resolution for discrete or non-discrete data, whichever we have.  Finally, we
  283.  **        make sure it's divisible by 72 for cleaner scaling.
  284.  **}
  285.  
  286. {$S Main}
  287. FUNCTION GetBestRsl :Integer;
  288.  
  289. VAR
  290.         err            : OSErr;
  291.         theRes, num    : Integer;
  292.         getRslData    : TGetRslBlk;
  293.  
  294. BEGIN
  295.  
  296. {Start off with our maximum resolution at 0, then call PrGeneral and parse our list
  297.  of returned values.}
  298.  
  299.         theRes := 0;
  300.         getRslData.iOpCode := getRslDataOp;
  301.  
  302.         PrGeneral(@getRslData);
  303.         err := getRslData.iError;
  304.  
  305. {If our printer only supports discrete resolutions, find the largest square one and
  306.  use that.  If our printer supports a range of resolutions, choose the smaller of the
  307.  maximum X and Y resolutions, then make it divisible by 72 for cleaner scaling.}
  308.  
  309.         IF (err = noErr) THEN
  310.             IF (getRslData.XRslRg.iMax = 0) AND (getRslData.YRslRg.iMax = 0) THEN
  311.                 BEGIN                                                                {Discrete resolutions.}
  312.                     FOR num := 1 TO getRslData.iRslRecCnt DO
  313.                         IF (getRslData.rgRslRec[num].iXRsl = getRslData.rgRslRec[num].iYRsl)
  314.                             AND (theRes < getRslData.rgRslRec[num].iXRsl) THEN
  315.                                 theRes := getRslData.rgRslRec[num].iXRsl;
  316.                 END
  317.             ELSE
  318.                 BEGIN                                                                {Variable resolutions.}
  319.                     IF (getRslData.XRslRg.iMax < getRslData.YRslRg.iMax) THEN
  320.                         theRes := (getRslData.XRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. X resolution.}
  321.                     ELSE
  322.                         theRes := (getRslData.YRslRg.iMax DIV 72) * 72            {Use multiple of 72 closest to max. Y resolution.}
  323.                 END;
  324.             
  325.  
  326. {In the unlikely event that PrGeneral fails and theRes is still 0, set it to 72.
  327.  This most likely is a supported resolution.  Finally return the best resolution we
  328.  could find.}
  329.  
  330.         IF theRes = 0 THEN theRes := 72;
  331.         GetBestRsl := theRes;
  332.  
  333. END;    {**  GetBestRsl  **}
  334.  
  335.  
  336. {*------ PrintStuff ----------------------------------------------------------------*}
  337. {**
  338.  **        PrintStuff will call all of the necessary Print Manager calls to print 
  339.  **        a document. It checks PrError after each Print Manager call. If an error 
  340.  **     is found, all of the Print Manager open calls (i.e. PrOpen, PrOpenDoc...) 
  341.  **        will have a corresponding close call before the error is posted to the user. 
  342.  **        You want to use this approach to make sure the Print Manager closes properly 
  343.  **        and all temporary memory is released.
  344.  **}
  345.  
  346. {$S Main}
  347. PROCEDURE PrintStuff;
  348.  
  349. VAR
  350.  
  351.     oldPort          : GrafPtr;
  352.     thePrRecHdl        : THPrint;
  353.     thePrPort        : TPPrPort;
  354.     theStatus        : TPrStatus;
  355.     rslData            : TSetRslBlk;
  356.     err                : OSErr;
  357.     prRsl            : Integer;
  358.     doPrGeneral        : Boolean;
  359.     hasPScript        : Boolean;
  360.     doSetLineWidth    : Boolean;
  361.     doScale            : Boolean;
  362.     setMHdl            : MenuHandle;
  363.     mark            : Char;
  364.     bestRsl            : Integer;
  365.  
  366. BEGIN
  367.  
  368. {Get our settings from the Settings menu and set up our Booleans appropriately.}
  369.  
  370.     setMHdl := GetMenuHandle(mSettings);
  371.     GetItemMark(setMHdl, iuseSetLine, mark);
  372.     doSetLineWidth := (mark <> Char(noMark));
  373.     GetItemMark(setMHdl, iusePrGen, mark);
  374.     doPrGeneral := (mark <> Char(noMark));
  375.     GetItemMark(setMHdl, iuseScale, mark);
  376.     doScale := (mark <> Char(noMark));
  377.  
  378.  
  379. {Get our current port and create a print handle.  If no errors,
  380.  do our PrOpen call and, if no errors again, get the default
  381.  settings for the current driver.}
  382.  
  383.     GetPort(oldPort);
  384.     thePrRecHdl := THPrint(NewHandle(sizeof(TPrint)));
  385.     
  386.     IF (MemError = noErr) THEN
  387.     BEGIN
  388.         PrOpen;
  389.         IF (PrError = noErr) THEN
  390.         BEGIN
  391.             PrintDefault(thePrRecHdl);
  392.  
  393.  
  394. {At this point we can check for PostScript drivers.  If the
  395.  high byte of the prStl dialog's wDev field is 3, we're using an Apple
  396.  PostScript driver.  Most third party drivers use this notation as well.
  397.  DTS does not recommend using this method unless you absolutely need to,
  398.  although it is a low-risk compatibility problem.  In other words, it's
  399.  the only way to check for PostScript now, but some day it will no
  400.  longer work.}
  401.  
  402.             hasPScript := BAND(thePrRecHdl^^.prStl.wDev, $0300) = $0300;
  403.  
  404.  
  405. {If this printer driver does not support PostScript, clear our SetLineWidth
  406.  flag.  This PicComment is currently only supported with PostScript drivers.}
  407.  
  408.             IF (NOT hasPScript) THEN
  409.                 doSetLineWidth := FALSE;
  410.  
  411.  
  412. {Set prRsl to 72 in case we aren't using PrGeneral or we get an error
  413.  when setting the best resolution.  If we are using PrGeneral, get the
  414.  best "square" resolution and set the printer to that.  If we have a
  415.  problem, we'll use a resolution of 72 dpi, which is the default for
  416.  most Macintosh printers.}
  417.  
  418.             prRsl := 72;
  419.  
  420.             IF (doPrGeneral) THEN
  421.             BEGIN
  422.  
  423.                 bestRsl := GetBestRsl;
  424.                 rslData.iOpCode := SetRslOp;
  425.                 rslData.hPrint := thePrRecHdl;
  426.                 rslData.iXRsl := bestRsl;
  427.                 rslData.iYRsl := bestRsl;
  428.                 PrGeneral(@rslData);
  429.                 err := rslData.iError;
  430.  
  431.                 IF (err = noErr) THEN prRsl := bestRsl;
  432.  
  433.             END;
  434.  
  435.  
  436. {If we still have no errors, give style and print job dialogs, then open a
  437.  document and its page.  Keep checking for those dang printer errors.}
  438.  
  439.             IF (PrError = noErr) THEN
  440.             BEGIN
  441.                 IF (PrStlDialog(thePrRecHdl)) THEN
  442.                 BEGIN
  443.                     IF (PrJobDialog(thePrRecHdl)) THEN 
  444.                     BEGIN
  445.                         thePrPort := PrOpenDoc(thePrRecHdl, NIL, NIL);
  446.                                
  447.                         IF (PrError = noErr) THEN
  448.                         BEGIN
  449.                             PrOpenPage(thePrPort, NIL);
  450.  
  451.  
  452. {If we're still running error-free, draw our test page.  rPage is the
  453.  printed page's dimensions, thePrPort is the printer port we're drawing
  454.  into, doPrGeneral, doSetLineWidth and doScale are flags relating to our
  455.  printing methods and prRsl is the resolution of our printer port.}
  456.  
  457.                             IF (PrError = noErr) THEN
  458.                                 DrawStuff(thePrRecHdl^^.prInfo.rPage, 
  459.                                           GrafPtr(thePrPort),
  460.                                           doPrGeneral, doSetLineWidth, doScale, prRsl);
  461.  
  462.  
  463. {When done, close our page and document and spool the document if necessary.  When
  464.  finshed, call PrClose to end the whole shabang.}
  465.  
  466.                             PrClosePage(thePrPort);
  467.                         END;
  468.                              
  469.                         PrCloseDoc(thePrPort);
  470.                              
  471.                         IF (thePrRecHdl^^.prJob.bJDocLoop = bSpoolLoop) and (PrError = noErr) THEN
  472.                             PrPicFile(thePrRecHdl, NIL, NIL, NIL, @theStatus);
  473.                     END;
  474.                 END;
  475.             END;
  476.         END;
  477.         
  478.         PrClose;
  479.  
  480.     END;
  481.  
  482. END;    {**  PrintStuff  **}
  483.  
  484.  
  485. {*------ Initialize ----------------------------------------------------------------*}
  486. {**
  487.  **        Initialize just handles necessary Toolbox initializing, setting our quitting 
  488.  **        flag to FALSE and installing our menus.
  489.  **}
  490.  
  491. {$S Initialize}
  492. PROCEDURE Initialize;
  493.  
  494. VAR
  495.     menuBar    : Handle;
  496.     
  497. BEGIN
  498.  
  499.     InitGraf(@qd.thePort);
  500.     InitFonts;
  501.     InitWindows;
  502.     InitMenus;
  503.     TEInit;
  504.     InitDialogs(NIL);
  505.     InitCursor;
  506.     FlushEvents(everyEvent, 0);    
  507.  
  508.     gQuitting := FALSE;
  509.  
  510.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  511.     IF (menuBar = NIL) THEN ExitToShell;    {should do real error stuff here.}
  512.     SetMenuBar(menuBar);                    {install menus}
  513.     DisposeHandle(menuBar);
  514.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  515.     DrawMenuBar;
  516.  
  517. END;    {**  Initialize  **}
  518.  
  519.  
  520. {$S _DataInit}
  521. PROCEDURE _DataInit; EXTERNAL;
  522.  
  523. {This routine is automatically linked in by the MPW Linker. This external
  524.  reference to it is done so that we can unload its segment, %A5Init.}
  525.  
  526.  
  527.  
  528. {*------ ToggleCMark ----------------------------------------------------------------*}
  529. {**
  530.  **        ToggleCMark is called when an item in the Settings menu is chosen. 
  531.  **        It simply toggles the checkmark next to the item passed.
  532.  **}
  533.  
  534. {$S Main}
  535. PROCEDURE ToggleCMark(menuItem: Integer);
  536.  
  537. VAR
  538.     setMHdl        : MenuHandle;
  539.     mark        : Char;
  540.  
  541. BEGIN
  542.  
  543.     setMHdl := GetMenuHandle(mSettings);
  544.     GetItemMark(setMHdl, menuItem, mark);
  545.     CheckItem(setMHdl, menuItem, (mark = Char(noMark)));
  546.  
  547. END;    {**  ToggleCMark  **}
  548.  
  549.  
  550. {*------ DoMenuCommand ----------------------------------------------------------------*}
  551. {**
  552.  **        DoMenuCommand is called when an item is chosen from the menu bar (after calling 
  553.  **        MenuSelect or MenuKey).  It does the right thing for each command.
  554.  **}
  555.  
  556. {$S Main}
  557. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  558.  
  559. VAR
  560.     menuID, menuItem    : INTEGER;
  561.     daRefNum            : INTEGER;
  562.     daName                : Str255;
  563.  
  564. BEGIN
  565.  
  566. {Get the menu ID and item ID.}
  567.  
  568.     menuID := HiWrd(menuResult);
  569.     menuItem := LoWrd(menuResult);
  570.  
  571.     CASE menuID OF
  572.         mApple:
  573.             CASE menuItem OF
  574.                 iAbout:                {bring up alert for About}
  575.                     (* We do nothing here... *);
  576.  
  577.                 OTHERWISE
  578.                 BEGIN        {all non-About items in this menu are DAs}
  579.                     GetMenuItemText(GetMenuHandle(mApple), menuItem, daName);
  580.                     daRefNum := OpenDeskAcc(daName);
  581.                 END;
  582.             END;
  583.  
  584.         mFile:                            {File Menu}
  585.             CASE menuItem OF
  586.                 iPrint:                        {-> Print Test Page.}
  587.                     PrintStuff;
  588.                 iQuit:
  589.                     gQuitting := TRUE;        {-> Quit}
  590.             END;
  591.  
  592.         mSettings:                        {Settings menu}
  593.             ToggleCMark(menuItem);            {-> Toggle checkmark for item.}
  594.     END;        
  595.     HiliteMenu(0);
  596.  
  597. END;    {**  DoMenuCommand  **}
  598.  
  599.  
  600. {*------ DoEvent ----------------------------------------------------------------*}
  601. {**
  602.  **        DoEvent handles incoming events for our app.  In this skimpy sample, we
  603.  **        only handle menu events and system clicks.
  604.  **}
  605.  
  606. {$S Main}
  607. PROCEDURE DoEvent;
  608.  
  609. VAR
  610.     part        : INTEGER;
  611.     key            : Char;
  612.     event        : EventRecord;
  613.     window        : WindowPtr;
  614.  
  615. BEGIN
  616.  
  617. {Repeatedly handle menu selecting events until our quit flag is set.}
  618.  
  619.     REPEAT
  620.         BEGIN
  621.             SystemTask;                                    {This must be called if using GetNextEvent}
  622.  
  623.             IF (GetNextEvent(everyEvent, event)) THEN
  624.                 CASE event.what OF
  625.                     mouseDown:
  626.                         BEGIN
  627.                             part := FindWindow(event.where, window);
  628.                             CASE part OF
  629.                                 inMenuBar:
  630.                                     DoMenuCommand(MenuSelect(event.where));
  631.                 
  632.                                 inSysWindow:
  633.                                     SystemClick(event, window);
  634.                             END;
  635.                         END;
  636.     
  637.                     keyDown, autoKey:
  638.                         BEGIN
  639.                             key := CHR(BAnd(event.message, charCodeMask));
  640.                             IF (BAnd(event.modifiers, cmdKey) <> 0) AND (event.what = keyDown) THEN
  641.                                 DoMenuCommand(MenuKey(key));
  642.                         END;
  643.                 END;
  644.         END;
  645.     UNTIL gQuitting;
  646.  
  647. END;    {**  DoEvent  **}
  648.  
  649.  
  650. {*------ Main ----------------------------------------------------------------*}
  651. {**
  652.  **        Main kickstarts our app.
  653.  **}
  654.  
  655. {$S Main}
  656. BEGIN
  657.  
  658.     {UnloadSeg(@_DataInit);    note that _DataInit must not be in Main!}
  659.     MaxApplZone;            {expand the heap so code segments load at the top}
  660.     Initialize;                {initialize the program}
  661.     {UnloadSeg(@Initialize);    note that Initialize must not be in Main!}
  662.     DoEvent;                {handle menu events until quitting.}
  663.  
  664. END.    {**  HairLines.  **}
  665.  
  666.